#include "stbiConvert.h"
#include <cstring>

#define STB_IMAGE_IMPLEMENTATION
#include "stb/stb_image.h"
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#include "stb/stb_image_resize.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb/stb_image_write.h"

namespace StbImage {

/**
 * @note
 */
bool getsize(unsigned char* oldData, const int oldsize, int& Width,
             int& Height) {
    int width, height, component;
    unsigned char* idata =
        stbi_load_from_memory(oldData, oldsize, &width, &height, &component, 0);
    if (idata == NULL) {
        return false;
    }
    Width = width;
    Height = height;
    stbi_image_free(idata);
    return true;
}

/**
 * @note    newWidth > newHeight 未处理
 */
bool resize(unsigned char* oldData, const int oldsize, int newWidth,
            int newHeight, int FillColor, std::string& newData) {
    int ret = 0;
    int width, height, component;
    unsigned char* idata =
        stbi_load_from_memory(oldData, oldsize, &width, &height, &component, 0);
    unsigned char* odata =
        (unsigned char*)malloc(newWidth * newHeight * component);
    memset(odata, FillColor, newWidth * newHeight * component);

    int offset = ((newWidth * newHeight * component) -
                  (newWidth * newWidth * component)) /
                 2;
    if (offset < 0) {
        stbi_image_free(odata);
        stbi_image_free(idata);
        std::cout << "Error: newWidt > newHeight" << std::endl;
        return false;
    }
    ret = stbir_resize(idata, width, height, 0, odata + offset, newWidth,
                       newWidth, 0, STBIR_TYPE_UINT8, component,
                       STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP,
                       STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_BOX,
                       STBIR_COLORSPACE_SRGB, nullptr);
    if (ret == 0) {
        stbi_image_free(odata);
        stbi_image_free(idata);
        std::cout << "Error: stbir_resize" << std::endl;
        return false;
    }
    ret = stbi_write_jpg_to_func(
        [](void* context, void* data, int size) -> void {
            std::string* str = (std::string*)context;
            str->append(reinterpret_cast<char*>(data), size);
        },
        &newData, newWidth, newHeight, component, odata, 0);
    if (ret == 0) {
        stbi_image_free(odata);
        stbi_image_free(idata);
        std::cout << "Error: stbi_write_jpg_to_func" << std::endl;
        return false;
    }
    stbi_image_free(odata);
    stbi_image_free(idata);
    return true;
}

bool resizepng(unsigned char* oldData, const int oldsize, int newWidth,
               int newHeight, int FillColor, std::string& newData) {
    int width, height, component;
    int ret = 0;
    unsigned char* idata =
        stbi_load_from_memory(oldData, oldsize, &width, &height, &component, 0);
    unsigned char* odata =
        (unsigned char*)malloc(newWidth * newHeight * component);
    memset(odata, FillColor, newWidth * newHeight * component);

    int offset = ((newWidth * newHeight * component) -
                  (newWidth * newWidth * component)) /
                 2;
    if (offset < 0) {
        stbi_image_free(odata);
        stbi_image_free(idata);
        std::cout << "Error: newWidt > newHeight" << std::endl;
        return false;
    }
    ret = stbir_resize(idata, width, height, 0, odata + offset, newWidth,
                       newWidth, 0, STBIR_TYPE_UINT8, component,
                       STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP,
                       STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_BOX,
                       STBIR_COLORSPACE_SRGB, nullptr);
    if (ret == 0) {
        stbi_image_free(odata);
        stbi_image_free(idata);
        std::cout << "Error: stbir_resize" << std::endl;
        return false;
    }
    ret = stbi_write_png_to_func(
        [](void* context, void* data, int size) -> void {
            std::string* str = (std::string*)context;
            str->append(reinterpret_cast<char*>(data), size);
        },
        &newData, newWidth, newHeight, component, odata, 0);
    if (ret == 0) {
        stbi_image_free(odata);
        stbi_image_free(idata);
        std::cout << "Error: stbi_write_png_to_func" << std::endl;
        return false;
    }
    stbi_image_free(odata);
    stbi_image_free(idata);
    return true;
}

bool ConvertGray(unsigned char* oldData, int oldSize, std::string& newData) {
    int width, height, component;
    unsigned char* tempData =
        stbi_load_from_memory(oldData, oldSize, &width, &height, &component, 0);
    unsigned char* srcData =
        (unsigned char*)malloc(sizeof(unsigned char) * width * height * 4);
    unsigned char* pSrc = srcData;
    unsigned char* pTemp = tempData;

    if (component == 4) {
        for (int j = 0; j < height; j++) {
            for (int i = 0; i < width; i++) {
                pSrc[0] = pTemp[2];
                pSrc[1] = pTemp[1];
                pSrc[2] = pTemp[0];
                pSrc[3] = pTemp[3];
                pSrc += 4;
                pTemp += 4;
            }
        }
    } else if (component == 3) {
        for (int j = 0; j < height; j++) {
            for (int i = 0; i < width; i++) {
                pSrc[0] = pTemp[2];
                pSrc[1] = pTemp[1];
                pSrc[2] = pTemp[0];
                pSrc[3] = 255;
                pSrc += 4;
                pTemp += 3;
            }
        }
        component = 4;
    } else if (component == 1) {
        for (int j = 0; j < height; j++) {
            for (int i = 0; i < width; i++) {
                int gray = *pTemp++;
                pSrc[0] = gray;
                pSrc[1] = gray;
                pSrc[2] = gray;
                pSrc[3] = 255;
                pSrc += 4;
            }
        }
        component = 4;
    } else {
        std::cout << "Error: component = " << component << std::endl;
        return false;
    }

    free(tempData);

    pSrc = srcData;
    for (int j = 0; j < height; j++) {
        for (int i = 0; i < width; i++) {
            int gray = (pSrc[0] + pSrc[1] + pSrc[2]) / 3;
            pSrc[0] = pSrc[1] = pSrc[2] = gray;
            pSrc += 4;
        }
    }

    stbi_write_jpg_to_func(
        [](void* context, void* data, int size) -> void {
            std::string* str = (std::string*)context;
            str->append(reinterpret_cast<char*>(data), size);
        },
        &newData, width, height, component, srcData, 0);

    free(srcData);
    return true;
}

/**
 * @note    newWidth > newHeight 未处理
 */
bool resize(unsigned char* inData, const int insize, int maxWidth,
            std::string& outData) {
    int Width = 0;
    int Height = 0;
    int imgmaxside = maxWidth;
    int imgalignment = 64;
    bool ret = false;

    if (!getsize(inData, insize, Width, Height)) {
        return false;
    }
    if (Width > Height) {
        Width = Height;
    }
    if ((Width > imgmaxside) || (Height > imgmaxside)) {
        if (Width > Height) {
            Height = Height * imgmaxside / Width;
            Width = imgmaxside;
        } else {
            Width = Width * imgmaxside / Height;
            Height = imgmaxside;
        }
        Height = (Height / imgalignment) * imgalignment;
        Width = (Width / imgalignment) * imgalignment;
        ret = resize(inData, insize, Width, Height, 0, outData);
    } else {
        outData = std::string((const char*)inData, insize);
        ret = true;
    }
    return ret;
}

void bmp2jpg(const char* pbmp, int pbmplen, std::string& pjpg) {
    int width, height, bpp, component;
    unsigned char* image = stbi_load_from_memory((unsigned char*)pbmp, pbmplen,
                                                 &width, &height, &bpp, 3);
    if (!image) {
        printf("Error loading BMP image: %s\n", stbi_failure_reason());
        return;
    }

    int quality = 90;
    int jpglen = 0;
    stbi_write_jpg_to_func(
        [](void* context, void* data, int size) -> void {
            std::string* str = (std::string*)context;
            str->append(reinterpret_cast<char*>(data), size);
        },
        image, width, height, component, &pjpg, 0);
    //   unsigned char *jpgdata = stbi_write_jpg_to_func(image, width * 3,
    //   width, height, 3, quality, &jpglen); if (jpgdata) {
    //     pjpg.assign((const char*)jpgdata, jpglen);
    // stbi_image_free(jpgdata);
    //   }

    stbi_image_free(image);
}
#define STB_IMAGE_WRITE_IMPLEMENTATION

typedef struct {
    uint8_t* data;
    size_t size;
} mem_buffer;

void write_func(void* context, void* data, int size) {
    mem_buffer* buffer = (mem_buffer*)context;
    size_t new_size = buffer->size + size;
    buffer->data = (uint8_t*)realloc(buffer->data, new_size);
    if (!buffer->data) {
        return;
    }
    memcpy(buffer->data + buffer->size, data, size);
    buffer->size = new_size;
}

char* bmp_to_jpg(const void* bmp_data, size_t bmp_size, int* len) {
    int width, height, channels;
    uint8_t* data = stbi_load_from_memory((const uint8_t*)bmp_data, bmp_size,
                                          &width, &height, &channels, STBI_rgb);
    if (!data) {
        return NULL;
    }
    mem_buffer buffer = {NULL, 0};
    stbi_write_jpg_to_func(write_func, &buffer, width, height, channels, data,
                           90);
    stbi_image_free(data);
    char* result = (char*)malloc(buffer.size + 1);
    if (result) {
        memcpy(result, buffer.data, buffer.size);
        result[buffer.size] = '\0';
        *len = buffer.size;
    }
    free(buffer.data);
    return result;
}

}  // namespace StbImage